package com.starsoft.quant.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.MapUtil;
import com.starsoft.quant.bean.QuantAcount;
import com.starsoft.quant.bean.QuantDetail;
import com.starsoft.quant.bean.QuantDetailId;
import com.starsoft.quant.bean.QuantModel;
import com.starsoft.quant.bean.QuantSummary;
import com.starsoft.quant.bean.QuantSummaryId;
import com.starsoft.quant.dao.QuantDetailDao;
import com.starsoft.quant.dao.QuantSummaryDao;
import com.starsoft.quant.executor.StrategyReport;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.dao.SmdcSecurityDao;
import com.starsoft.trade.bean.TradeSummary;
import com.starsoft.trade.dao.TradeSummaryDao;

@Service
public class QuantReportService {
	
	Logger logger = LoggerFactory.getLogger(QuantReportService.class);
	
	@Autowired
	QuantDetailDao detailDao;
	
	@Autowired
	QuantSummaryDao quantSummaryDao;
	
	@Autowired
	SmdcSecurityDao securityDao;
	
	@Autowired
	TradeSummaryDao tradeSummaryDao;
	
	public StrategyReport getReport(Integer acountId, Date date){
		List<QuantDetail> details = detailDao.getDetails(acountId, date);
		QuantSummary summary = quantSummaryDao.get(acountId, date);
		StrategyReport strategyReport = new StrategyReport();
		strategyReport.setSummary(summary);
		strategyReport.setDetails(details);
		return strategyReport;
	}
	
	public QuantSummary getSummary(Integer acountId, Date date){
		return quantSummaryDao.get(acountId, date);
	}

	public List<QuantDetail> getDetailsBetween(Integer acountId, Date beginDate, Date endDate){
		return detailDao.getDetailsBetween(acountId, beginDate, endDate);
	}
	
	public Map<String, Object> convertReport(StrategyReport report, QuantModel quantModel){
		Map<String, Object> result = new HashMap<String, Object>();
		Map<String, Object> summary =MapUtil.toMap(report.getSummary());
		summary.put("summaryDate", report.getSummary().getId().getSummaryDate());
		result.put("summary", summary);
		
		List<QuantDetail> details = report.getDetails();
		List<Map<String, Object>> jsonDetails = new ArrayList<>();
		List<Map<String, Object>> tradeSuggest = new ArrayList<>();
		for(QuantDetail detail:details){
			Map<String, Object> one = MapUtil.toMap(detail);
			String secId = detail.getId().getSecId();
			SmdcSecurity smdcSecurity = securityDao.get(secId);
			String secName =secId;
			if(smdcSecurity!=null) {
				secName = smdcSecurity.getSecName();
			}			
			one.put("secId", secId);
			one.put("secName", secName);
			convertType(one, quantModel);
			
			if(detail.getTrade()!=null){
				Map<String, Object> trade= new HashMap<>();
				trade.put("secId", secId);
				trade.put("secName", secName);
				trade.put("tradeNum",detail.getTrade());
				trade.put("price",detail.getPrice());
				tradeSuggest.add(trade);
			}
			jsonDetails.add(one);
		}
		result.put("details", jsonDetails);
		result.put("tradeSuggest", tradeSuggest);
		return result;
	}
	
	private void convertType(Map<String, Object> one, QuantModel quantModel) {
		Map<String, Object> model = MapUtil.toMap(quantModel);
		String key = "col";
		for (int i = 1; model.containsKey(key + i); i++) {
			String colTypeName = key + i + "t";
			String colType = (String) model.get(colTypeName);
			if ("double".equalsIgnoreCase(colType) && one.get(key + i) != null) {
				try {
					String value = (String) one.get(key + i);
					one.put(key + i, Double.parseDouble(value));
				} catch (Exception e) {
					logger.warn("Convert to double failed :{}", e.getMessage());
				}
				
			}
		}
	}
	
	public void saveSummary(QuantSummary summary){
		quantSummaryDao.saveOrUpdate(summary);
	}
	
	public void saveDetails(List<QuantDetail> details) {
		if (details.size() > 0) {
			QuantDetailId id = details.get(0).getId();
			detailDao.delete(id.getAcountId(), id.getDate());
			for (QuantDetail detail : details) {
				detailDao.save(detail);
			}
		}
	}
	
	public void deleteReportBetween(Integer acountId, Date beginDate, Date endDate) {
		quantSummaryDao.deleteReportBetween(acountId, beginDate, endDate);
		detailDao.deleteReportBetween(acountId, beginDate, endDate);
	}
	
	public QuantSummary updateAnalysis(Integer acountId, Date asofDate) {
		QuantAcount quantAcount = quantSummaryDao.get(QuantAcount.class, acountId);
		QuantSummary summary = quantSummaryDao.get(acountId, asofDate);
		if(summary == null){
			summary = new QuantSummary(new QuantSummaryId(acountId, asofDate));
		}
		Date date1m  = DateUtil.bumpDate(asofDate, 0, -1, 0);
		Date date3m  = DateUtil.bumpDate(asofDate, 0, -3, 0);
		Date date6m  = DateUtil.bumpDate(asofDate, 0, -6, 0);
		Date date12m  = DateUtil.bumpDate(asofDate, 0, -12, 0);
		boolean ev1m=false, ev3m=false, ev6m=false, ev12m=false;
		List<TradeSummary> summaryList = tradeSummaryDao.getAcountBetween(acountId, quantAcount.getStartDate() ,asofDate);
		double years = DateUtil.getYears(quantAcount.getStartDate(), asofDate);
		
		SimpleRegression regression = new SimpleRegression(); 
		StandardDeviation stdev = new StandardDeviation();
		
		List<Double> baseProfitList = new ArrayList<Double>(summaryList.size());
		List<Double> profitList = new ArrayList<Double>(summaryList.size());
		double[] profitArray = null;
		double[] baseProfitArray = null;
		for (int i = summaryList.size() -1; i >= 0; i--) {
			TradeSummary summaryHis = summaryList.get(i);
			if(asofDate.equals(summaryHis.getId().getAsofdate())){
				summary.setProfitDay(summaryHis.getProfitDay());
				summary.setBaseProfitDay(summaryHis.getBaseProfitDay());
			} if(date1m.after(summaryHis.getId().getAsofdate()) && !ev1m){
				ev1m =true;
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit1m(multiyCal(profitArray));
				summary.setBaseProfit1m(multiyCal(baseProfitArray));
				summary.setVolability1m(stdev.evaluate(profitArray));
				summary.setAlpha1m(regression.getIntercept()*250);
				summary.setBeta1m(regression.getSlope());
			}else if(date3m.after(summaryHis.getId().getAsofdate()) && !ev3m){
				ev3m =true;
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit3m(multiyCal(profitArray));
				summary.setBaseProfit3m(multiyCal(baseProfitArray));
				summary.setVolability3m(stdev.evaluate(profitArray));
				summary.setAlpha3m(regression.getIntercept()*250);
				summary.setBeta3m(regression.getSlope());
			} else if(date6m.after(summaryHis.getId().getAsofdate()) && !ev6m){
				ev6m =true;
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit6m(multiyCal(profitArray));
				summary.setBaseProfit6m(multiyCal(baseProfitArray));
				summary.setVolability6m(stdev.evaluate(profitArray));
				summary.setAlpha6m(regression.getIntercept()*250);
				summary.setBeta6m(regression.getSlope());
			} else if(date12m.after(summaryHis.getId().getAsofdate()) && !ev12m){
				ev12m =true;
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit12m(multiyCal(profitArray));
				summary.setBaseProfit12m(multiyCal(baseProfitArray));
				summary.setVolability12m(stdev.evaluate(profitArray));
				summary.setAlpha12m(regression.getIntercept()*250);
				summary.setBeta12m(regression.getSlope());
			}
			baseProfitList.add(summaryHis.getBaseProfitDay());
			profitList.add(summaryHis.getProfitDay());
			regression.addData(summaryHis.getBaseProfitDay(), summaryHis.getProfitDay());
		}
		profitArray = listToArray(profitList);
		baseProfitArray = listToArray(baseProfitList);
		summary.setProfit(multiyCal(profitArray));
		summary.setBaseProfit(multiyCal(baseProfitArray));
		if(summaryList.size()>2){
			summary.setAlpha(regression.getIntercept()*250);
			summary.setBeta(regression.getSlope());
			summary.setVolability(stdev.evaluate(profitArray));
		}
		if(years>0.1){
			summary.setProfitYear(Math.pow(summary.getProfit() + 1, 1 / years) - 1);
		}
		quantSummaryDao.saveOrUpdate(summary);
		return summary;
	}

	private double[] listToArray(List<Double> datas) {
		double[] result = new double[datas.size()];
		for (int i = 0; i < datas.size(); i++) {
			result[i] = datas.get(i);
		}
		return result;
	}
	
	private double multiyCal(double[] values){
		double result = 1;
		for(int i=values.length-1;i>=0;i--){
			result *=(1+values[i]);
		}
		return result -1;
	}
}
